package cCommand

import (
	"fmt"
	"reflect"
	"slices"
	"sync"

	"github.com/robfig/cron/v3"
)

const (
	instance_name_format = "%s.%s"
	command_name_format  = "%s.%s.%s"
)

type CommandContainer struct {
	lock      sync.RWMutex
	cron      *cron.Cron
	stop      chan cron.EntryID
	instances map[string]any

	commands         map[string]map[string]map[string]string
	interfaceMethods []string
}

func (i *CommandContainer) Save(instance any) bool {
	i.lock.Lock()
	defer i.lock.Unlock()

	app, name := instance.(CommandInterface).CommandName()
	index := fmt.Sprintf(instance_name_format, app, name)
	i.instances[index] = instance

	if _, ok := i.commands[app]; !ok {
		i.commands[app] = make(map[string]map[string]string)
	}

	if _, ok := i.commands[app][name]; !ok {
		i.commands[app][name] = make(map[string]string)
	}

	instanceType := reflect.TypeOf(instance)
	methodCount := instanceType.NumMethod()
	for k := 0; k < methodCount; k++ {
		methodType := instanceType.Method(k)
		methodName := methodType.Name
		if slices.Contains(i.interfaceMethods, methodName) {
			continue
		}
		i.commands[app][name][methodName] = methodType.Type.String()
	}

	return true
}

func (i *CommandContainer) UpdateCommands() {
	i.lock.Lock()
	defer i.lock.Unlock()

	for app, commands := range i.commands {
		for command, methods := range commands {
			for method := range methods {
				remove := true
				for _, item := range command_config.Commands {
					if item.App == app && item.Command == command && item.Method == method {
						remove = false
						break
					}
				}
				if remove {
					delete(i.commands[app][command], method)
				}
			}
			if len(i.commands[app][command]) == 0 {
				delete(i.commands[app], command)
			}
		}
		if len(i.commands[app]) == 0 {
			delete(i.commands, app)
		}
	}
}

func (i *CommandContainer) Get(name string) any {
	i.lock.RLock()
	defer i.lock.RUnlock()

	return i.instances[name]
}

func (i *CommandContainer) Remove(name string) bool {
	i.lock.Lock()
	defer i.lock.Unlock()

	delete(i.instances, name)

	return true
}

func (i *CommandContainer) Is(instance any) bool {
	return reflect.TypeOf(instance).Implements(reflect.TypeOf((*CommandInterface)(nil)).Elem())
}

func (i *CommandContainer) Range(f func(instance any)) {
	i.lock.RLock()
	defer i.lock.RUnlock()

	for _, item := range i.instances {
		f(item)
	}
}

var container = &CommandContainer{
	lock:      sync.RWMutex{},
	cron:      nil,
	stop:      make(chan cron.EntryID, 1000),
	instances: make(map[string]any),

	commands:         make(map[string]map[string]map[string]string),
	interfaceMethods: []string{"CommandName"},
}
